package install

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"zcommom/commom"
	"zcommom/zlog"

	"github.com/kardianos/service"
)

// Keep it in sync with the template from service_sysv_linux.go file
// Use "ps | grep -v grep | grep $(get_pid)" because "ps PID" may not work on OpenWrt
const SysvScript = `#!/bin/sh
# For RedHat and cousins:
# chkconfig: - 99 01
# description: {{.Description}}
# processname: {{.Path}}
### BEGIN INIT INFO
# Provides:          {{.Path}}
# Required-Start:
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: {{.DisplayName}}
# Description:       {{.Description}}
### END INIT INFO
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
name=$(basename $(readlink -f $0))
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
get_pid() {
    cat "$pid_file"
}
is_running() {
    [ -f "$pid_file" ] && ps | grep -v grep | grep $(get_pid) > /dev/null 2>&1
}
case "$1" in
    start)
        if is_running; then
            echo "Already started"
        else
            echo "Starting $name"
            {{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
            $cmd >> "$stdout_log" 2>> "$stderr_log" &
            echo $! > "$pid_file"
            if ! is_running; then
                echo "Unable to start, see $stdout_log and $stderr_log"
                exit 1
            fi
        fi
    ;;
    stop)
        if is_running; then
            echo -n "Stopping $name.."
            kill $(get_pid)
            for i in $(seq 1 10)
            do
                if ! is_running; then
                    break
                fi
                echo -n "."
                sleep 1
            done
            echo
            if is_running; then
                echo "Not stopped; may still be shutting down or shutdown may have failed"
                exit 1
            else
                echo "Stopped"
                if [ -f "$pid_file" ]; then
                    rm "$pid_file"
                fi
            fi
        else
            echo "Not running"
        fi
    ;;
    restart)
        $0 stop
        if is_running; then
            echo "Unable to stop, will not attempt to start"
            exit 1
        fi
        $0 start
    ;;
    status)
        if is_running; then
            echo "Running"
        else
            echo "Stopped"
            exit 1
        fi
    ;;
    *)
    echo "Usage: $0 {start|stop|restart|status}"
    exit 1
    ;;
esac
exit 0
`

const SystemdScript = `[Unit]
Description={{.Description}}
ConditionFileIsExecutable={{.Path|cmdEscape}}
{{range $i, $dep := .Dependencies}} 
{{$dep}} {{end}}
[Service]
LimitNOFILE=65536
StartLimitInterval=5
StartLimitBurst=10
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
{{if .UserName}}User={{.UserName}}{{end}}
{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}}
{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}}
{{if and .LogOutput .HasOutputFileSupport -}}
StandardOutput=file:/var/log/{{.Name}}.out
StandardError=file:/var/log/{{.Name}}.err
{{- end}}
Restart=always
RestartSec=120
[Install]
WantedBy=multi-user.target
`

//回调接口
type paramCallBack interface {
	OnStart()
	OnStop()
}

type program struct {
	exit    chan struct{}
	service service.Service
	proCall paramCallBack
}

//开始启动服务
func (p *program) Start(s service.Service) error {
	_status, _ := s.Status()
	zlog.Info("param start status=", _status)

	go p.run()

	return nil
}

//停止服务
func (p *program) Stop(s service.Service) error {
	_status, _ := s.Status()
	zlog.Info("param Stop status=", _status)

	close(p.exit)
	p.proCall.OnStop()

	if service.Interactive() {
		zlog.Info("stop service.Interactive")
		os.Exit(0)
	}
	return nil
}

//启动服务后异步操作其他
func (p *program) run() {
	defer func() {
		if err := recover(); err != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			zlog.Warn(" panic serving %v: %v\n%s", err, string(buf))
		}
	}()

	zlog.Info("开始执行程序")
	p.proCall.OnStart()
	select {
	case <-p.exit:
		zlog.Warn("stop...")
	}
}

type config struct {
	Name, DisplayName, Description string
}

//配置文件对象
var cc config

//服务对象
var ss service.Service

//项目对象
var prg program

//默认不输入任何东西，在终端执行，
//service 是后台服务
//开启执行start name:可执行名称，名称介绍，详细介绍，执行成功回调
func Run(name, displayName, description string, parCall paramCallBack) {
	cc = config{Name: name, DisplayName: displayName, Description: description}

	prg := &program{
		exit: make(chan struct{}),
	}

	svcConfig := getConfig("")
	var err error
	ss, err = service.New(prg, &svcConfig)
	if err != nil {
		//一定要通过服务,否则报错
		log.Fatal(err)
	}
	prg.service = ss
	prg.proCall = parCall
	arg := ""
	//是通过systemctl start |stop
	if len(os.Args) > 1 {
		arg = os.Args[1]
	}
	zlog.Info("arg ", arg)
	status, _ := ss.Status()
	zlog.Info("status ", status)
	switch arg {
	case "install":
		install()
		return
	case "stop":
		optsion(arg)
		break
	case "start", "restart":
		optsion(arg)
		return
	case "status":
		getStatus()
		break
	case "uninstall":
		uninstall()
		return
	case "service": //守护进程启动后，会调用这个
		_ = ss.Run()
		break
	case "help", "h":
		fmt.Println("Please enter :start|stop|install|status|uninstall|service")
		return
	default:
		_ = ss.Run()
		break
	}

}

//初始化操作
func install() {

	svcConfig := getConfig("install")
	s, err := service.New(&prg, &svcConfig)
	//服务无法运行
	if err != nil {
		log.Fatal(err)
		return
	}
	err = service.Control(s, "install")
	if err != nil {
		fmt.Println("install error ", err.Error())
		zlog.Info("install error ", err.Error())
	} else {
		zlog.Info("install success ")
	}

	if service.Platform() == "unix-systemv" {

		confPath := "/etc/init.d/" + cc.Name
		os.Symlink(confPath, "/etc/rc.d/S90"+cc.Name)
		os.Symlink(confPath, "/etc/rc.d/K02"+cc.Name)
	}
}

//"start", "restart", "stop":
func optsion(arg string) {
	zlog.Info("Platform", service.Platform())
	if service.Platform() == "unix-systemv" {
		zlog.Info("unix-systemv service")
		cmd := exec.Command("/etc/init.d/"+cc.Name, arg)
		err := cmd.Run()
		if err != nil {
			log.Fatal(err)
		}
		return
	}
	err := service.Control(ss, arg)
	if err != nil {
		fmt.Println(arg, " error ", err.Error())
		zlog.Info(arg, " error ", err.Error())
	} else {
		fmt.Println(arg, " success")
		zlog.Info(arg, " success")
	}
}

//卸载
func uninstall() {
	err := service.Control(ss, "uninstall")
	if err != nil {
		fmt.Println("uninstall error :", err.Error())
		zlog.Info("uninstall error ", err)
	} else {
		zlog.Info("uninstall success ")
	}
	zlog.Info("Platform", service.Platform())
	if service.Platform() == "unix-systemv" {
		zlog.Info("unix-systemv service")
		os.Remove("/etc/rc.d/S90" + cc.Name)
		os.Remove("/etc/rc.d/K02" + cc.Name)
	}
}

func getStatus() string {

	fmt.Println(cc.Name, " status no running")

	return "error"
}

///etc/systemd/system
//得到install需要脚步name:可执行文件名称,displayName简单的提示,description详细描述 ,args操作
func getConfig(arg string) (config service.Config) {
	options := make(service.KeyValue)
	config.Name = cc.Name
	config.DisplayName = cc.DisplayName
	config.Description = cc.Description
	config.Option = options
	config.Arguments = append(config.Arguments, "service")
	if !commom.IsWindows() {
		config.Dependencies = []string{
			"Requires=network.target",
			"After=network-online.target syslog.target"}
		config.Option["SystemdScript"] = SystemdScript
		config.Option["SysvScript"] = SysvScript
	}

	if arg == "install" {
		binPath := commom.GetAppPath() + "/" + cc.Name
		zlog.Info("getConfig GetAppPath=", binPath)
		config.Executable = binPath
	}

	return config
}

//得到可执行文件名称
func Executable(binName string) string {
	//binPath := copyStaticFile(common.GetAppPath(), binName)
	//return binPath
	bingPath := commom.GetAppPath() + "/" + binName

	return bingPath
}
